/*
 * $Id: JMapGrid.java 62 2010-02-13 16:05:39Z aaron.day $
 */
package org.crazydays.gameplan.map.swing;


import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;

import org.crazydays.gameplan.map.Geometry;
import org.crazydays.gameplan.map.GridType;
import org.crazydays.gameplan.swing.AbstractScrollableComponent;

/**
 * JMapGrid
 */
public class JMapGrid
    extends AbstractScrollableComponent
{
    /** serial version uid */
    public final static long serialVersionUID =
        "$Id: JMapGrid.java 62 2010-02-13 16:05:39Z aaron.day $".hashCode();

    /** lines */
    protected BufferedImage grid;

    /**
     * AbstractMapGrid constructor.
     * 
     * @param geometry Geometry
     */
    public JMapGrid(Geometry geometry)
    {
        setSize(geometry.getWidth(), geometry.getHeight());
        grid =
            new BufferedImage(geometry.getWidth(), geometry.getHeight(),
                BufferedImage.TYPE_4BYTE_ABGR);

        buildGrid(geometry);
    }

    /**
     * Build grid.
     * 
     * @param geometry Geometry
     */
    protected void buildGrid(Geometry geometry)
    {
        switch (geometry.getType()) {
            case GridType.SQUARE:
                buildSquareGrid(geometry);
                break;
            case GridType.HEXAGON:
                buildHexagonGrid(geometry);
                break;
            case GridType.ISOMETRIC:
                buildIsometricGrid(geometry);
                break;
            default:
                throw new UnsupportedOperationException("Unknown grid type: "
                    + geometry.getType());
        }
    }

    /**
     * Build square grid.
     * 
     * @param geometry Geometry
     */
    protected void buildSquareGrid(Geometry geometry)
    {
        Graphics graphics = grid.getGraphics();

        // vertical lines
        for (int i = 0; i < grid.getWidth(); i += geometry.getSize()) {
            graphics.drawLine(i, 0, i, grid.getHeight());
        }

        // horizontal lines
        for (int i = 0; i < grid.getHeight(); i += geometry.getSize()) {
            graphics.drawLine(0, i, grid.getWidth(), i);
        }
    }

    /**
     * Build hexagon grid.
     * 
     * @param geometry Geometry
     */
    protected void buildHexagonGrid(Geometry geometry)
    {
        Graphics graphics = grid.getGraphics();

        for (int i = 0; i < grid.getWidth(); i += geometry.getSize() * 3) {
            for (int j = 0; j < grid.getHeight(); j +=
                (Math.cos(Math.PI * 1 / 6) * geometry.getSize()) * 2) {

                // current row
                drawHexMapLine(graphics, i, j, geometry.getSize());

                // next row
                drawHexMapLine(graphics, i + (geometry.getSize() * 3 / 2),
                    (int) (j + Math.cos(Math.PI * 1 / 6) * geometry.getSize()),
                    geometry.getSize());
            }
        }
    }

    /**
     * Draw a hexagon map.
     * 
     * @param graphics Graphics
     * @param x X
     * @param y Y
     * @param size Size
     */
    protected void drawHexMapLine(Graphics graphics, int x, int y, int size)
    {
        int[] xs = new int[7];
        int[] ys = new int[7];

        xs[0] = xs[6] = x + (int) (Math.sin(Math.PI * 1 / 6) * size);
        ys[0] = ys[6] = y + (int) (Math.cos(Math.PI * 1 / 6) * size);

        xs[1] = x + size;
        ys[1] = y;

        xs[2] = x + (int) (Math.sin(Math.PI * 1 / 6) * size);
        ys[2] = y - (int) (Math.cos(Math.PI * 1 / 6) * size);

        xs[3] = x - (int) (Math.sin(Math.PI * 1 / 6) * size);
        ys[3] = y - (int) (Math.cos(Math.PI * 1 / 6) * size);

        xs[4] = x - size;
        ys[4] = y;

        xs[5] = x - (int) (Math.sin(Math.PI * 1 / 6) * size);
        ys[5] = y + (int) (Math.cos(Math.PI * 1 / 6) * size);

        // for (int i = 0; i < 6; i++) {
        // System.out.println("point[" + i + "]: (" + xs[i] + ","
        // + ys[i] + ")");
        // }

        graphics.drawPolyline(xs, ys, 7);
    }

    /**
     * Build isometric geometry.
     * 
     * @param geometry Geometry
     */
    protected void buildIsometricGrid(Geometry geometry)
    {
        Graphics graphics = grid.getGraphics();

        int ydiff = (int) (grid.getHeight() * Math.sin((Math.PI * 1 / 6)));

        // slash lines
        for (int i = 0; i < (grid.getHeight() + ydiff); i += geometry.getSize()) {
            graphics.drawLine(0, i, grid.getWidth(), i - ydiff);
        }

        // backslash lines
        for (int i = roundDown(-ydiff, geometry.getSize()); i < grid
            .getHeight(); i += geometry.getSize()) {
            graphics.drawLine(0, i, grid.getWidth(), i + ydiff);
        }
    }

    /**
     * Round down to the nearest.
     * 
     * @param value Value
     * @param nearest Nearest
     * @return Value
     */
    private int roundDown(int value, int nearest)
    {
        int times = value / nearest;
        int left = value % nearest;

        return (times * nearest) - (left > 0 ? left : 0);
    }

    /**
     * Get preferred size.
     * 
     * @return Size
     * @see java.awt.Component#getPreferredSize()
     */
    public Dimension getPreferredSize()
    {
        return new Dimension(grid.getWidth(), grid.getHeight());
    }

    /**
     * Paint image.
     * 
     * @param graphics Graphics
     * @see java.awt.Component#paint(java.awt.Graphics)
     */
    public void paint(Graphics graphics)
    {
        graphics.drawImage(grid, 0, 0, null);
    }
}
